群益 API 是利用自己開發的程式,結合群益 API 在群益券商下單的一種方式,通常是做程式交易下單,或是單純讀取報價也行。
此範例教學是我看群益 API 的範例,再重新做出我要的程式交易功能。
這個章節是利用 API 取得商品報價的範例,報價回傳即時成交資訊、Tick 以及最佳 5 檔回傳。
如果需要實際操作才好學習的話,在最後我會提供原始碼範例下載連結,可以在自己電腦執行看看。
我在寫程式碼時盡量把註解說明清楚一點,這樣可以幫助想學習的人看懂一些,完整的功能還是要看官方的文件和範例喔。
在往下看之前,我還是先提醒一下,我設計的介面是為了教學而簡單設計的,給大家學習參考而已,完整的功能還是要看官方的文件和範例喔。
群益 API 官網範例下載位置: 群益 API
此篇文章的內容是接續上一篇文章繼續說明,在上一篇裡面,我們完成了新建專案、註冊元件以及群益帳號密碼登入測試。
在登入成功之後,可以儲存帳號密碼,在接下來取得報價之前,就會讀取帳號密碼自動登入。
打開 VS 之後,可以照我的畫面,拉出這個設計檢視。
上面紅色字是控制項與 ID,好方便以下程式碼對應。
主要功能是用下拉來指定商品,再拉出 3 個 DataGridView ,分別放最新報價、Tick 以及最佳 5 檔。
最後在上面放上按鈕來啟動報價。
下方的文字區就顯示過程訊息。
閱讀前提醒一下,我只會將重點語法拿出來講解喔,想要看完整程式碼及操作的話,可以到文末下載範例來看。
在群益 API 呼叫之前,都需要先初始化物件。對於報價訂閱後,還需要宣告接受事件。
然後在初始化動作裡面,我特別增加了一些交易商品。
// 初始化物件
m_pSKCenter = new SKCenterLib();
m_pSKReply = new SKReplyLib();
m_SKQuoteLib = new SKQuoteLib();
// 註冊公告事件
m_pSKReply.OnReplyMessage += new _ISKReplyLibEvents_OnReplyMessageEventHandler(this.m_pSKReply_OnAnnouncement);
// 國內報價連線狀態事件
m_SKQuoteLib.OnConnection += new _ISKQuoteLibEvents_OnConnectionEventHandler(m_SKQuoteLib_OnConnection);
// 國內報價事件
m_SKQuoteLib.OnNotifyQuoteLONG += new _ISKQuoteLibEvents_OnNotifyQuoteLONGEventHandler(m_SKQuoteLib_OnNotifyQuoteLONG);
// 國內 Tick 回傳事件
m_SKQuoteLib.OnNotifyTicksLONG += new _ISKQuoteLibEvents_OnNotifyTicksLONGEventHandler(m_SKQuoteLib_OnNotifyTicks);
// 國內 Best5 回傳事件
m_SKQuoteLib.OnNotifyBest5LONG += new _ISKQuoteLibEvents_OnNotifyBest5LONGEventHandler(m_SKQuoteLib_OnNotifyBest5);
// 加入商品
cboCommID.Items.Add(new ComboboxItem("TX00", "大台指期近月"));
cboCommID.Items.Add(new ComboboxItem("MTX00", "小台指期近月"));
cboCommID.Items.Add(new ComboboxItem("TE00", "電子期近月"));
cboCommID.Items.Add(new ComboboxItem("TF00", "金融期近月"));
cboCommID.SelectedIndex = 0;
要取得報價之前,需要先連線,等待連線成功的回應之後。才能繼續下一步。
// 國內報價連線
nCode = m_SKQuoteLib.SKQuoteLib_EnterMonitorLONG();
txtMessage.AppendText(GetMessage("國內報價連線", nCode) + "\n");
if (nCode != 0)
{
return;
}
等待報價連線回應事件
/// <summary>
/// 國內報價連線回應事件
/// </summary>
/// <param name="nKind"></param>
/// <param name="nCode"></param>
void m_SKQuoteLib_OnConnection(int nKind, int nCode)
{
if (nKind == 3001)
{
if (nCode == 0)
{
// 連線中
lblTwSignal.ForeColor = Color.Blue;
lblTwSignal.Text = "連線狀態:連線中";
}
}
else if (nKind == 3002)
{
// 連線中斷
lblTwSignal.ForeColor = Color.Red;
lblTwSignal.Text = "連線狀態:中斷";
btnStartQuote.Enabled = true;
}
else if (nKind == 3003)
{
// 連線成功
lblTwSignal.ForeColor = Color.Green;
lblTwSignal.Text = "連線狀態:正常";
// 訂閱最新報價
RequestQuote();
// 訂閱 Tick & Best5
RequestTickBest5();
btnStartQuote.Enabled = false;
}
else if (nKind == 3021)
{
//網路斷線
lblTwSignal.ForeColor = Color.DarkRed;
}
}
在連線回應裡面當狀態為連線成功的時候,我們就直接呼叫訂閱報價、Tick 與最佳 5 檔。
取得最新報價是要回傳開盤、最高、最低、成交價、成交量等相關資料,並且當有最新價格時,就要觸發回應事件。
程式碼分為取得商品相關資訊以及訂閱報價回傳事件。
/// <summary>
/// 訂閱最新報價
/// </summary>
private void RequestQuote()
{
// 取回商品報價的相關資訊
SKSTOCKLONG pSKStockLONG = new SKSTOCKLONG();
nCode = m_SKQuoteLib.SKQuoteLib_GetStockByNoLONG(ComboUtil.GetItem(cboCommID).Value, ref pSKStockLONG);
txtMessage.AppendText(GetMessage("取回商品報價的相關資訊", nCode) + "\n");
// 將報價資訊物件輸出在 DataGridView
onUpdateQuote(pSKStockLONG);
if (nCode != 0)
{
// 發生錯誤
return;
}
// 更新價格小數位
dDigitNum = (Math.Pow(10, pSKStockLONG.sDecimal));
//訂閱商品即時報價,訂閱後等待 OnNotifyQuoteLONG 事件回報
nCode = m_SKQuoteLib.SKQuoteLib_RequestStocks(ref sPage, ComboUtil.GetItem(cboCommID).Value);
txtMessage.AppendText(GetMessage("訂閱商品即時報價", nCode) + "\n");
}
當有新的報價時,就會觸發以下程式碼:
/// <summary>
/// 國內報價回應事件
/// </summary>
/// <param name="sMarketNo"></param>
/// <param name="nStockIdx"></param>
void m_SKQuoteLib_OnNotifyQuoteLONG(short sMarketNo, int nStockIdx)
{
// 報價資訊物件
SKSTOCKLONG pSKStockLONG = new SKSTOCKLONG();
// 取得最新報價寫入報價資訊物件
m_SKQuoteLib.SKQuoteLib_GetStockByIndexLONG(sMarketNo, nStockIdx, ref pSKStockLONG);
// 將報價資訊物件輸出在 DataGridView
onUpdateQuote(pSKStockLONG);
}
Tick 是每次成交的價格記錄,回傳的資訊就是基本的時間、成交價、數量、委買賣等資訊。
要訂閱 Tick 以及最佳 5 檔是同一個呼叫函式,只要呼叫這函式後,就可以接收到 2 種回應事件。
/// <summary>
/// 訂閱 Tick & Best5
/// </summary>
private void RequestTickBest5()
{
//訂閱 Tick & Best5,訂閱後等待 OnNotifyTicks 及 OnNotifyBest5 事件回報
nCode = m_SKQuoteLib.SKQuoteLib_RequestTicks(ref sPage, ComboUtil.GetItem(cboCommID).Value);
txtMessage.AppendText(GetMessage("訂閱 Tick & Best5", nCode) + "\n");
}
建立 Tick 回應事件等待觸發。
/// <summary>
/// 國內 Tick 回傳事件
/// </summary>
void m_SKQuoteLib_OnNotifyTicks(short sMarketNo, int nStockIdx, int nPtr, int nDate, int lTimehms, int lTimemillismicros, int nBid, int nAsk, int nClose, int nQty, int nSimulate)
{
DataRow dr = null;
// 轉化時間格式為 yyyy/MM/dd HH:mm:ss.sss
string date = (nDate.ToString().Substring(0, 4) + "/" + nDate.ToString().Substring(4, 2) + "/" + nDate.ToString().Substring(6));
string time = lTimehms.ToString("000000").Substring(0, 2) + ":" + lTimehms.ToString("000000").Substring(2, 2) + ":" + lTimehms.ToString("000000").Substring(4) + "." + lTimemillismicros.ToString("000000").Substring(0, 3);
if (dtTick == null)
{
// 報價物件寫入 Datatable
dtTick = new DataTable();
dtTick.Columns.Add("TickName");
dtTick.Columns.Add("TickValue");
dr = dtTick.NewRow();
dr["TickName"] = "日期";
dr["TickValue"] = date;
dtTick.Rows.Add(dr);
dr = dtTick.NewRow();
dr["TickName"] = "時間";
dr["TickValue"] = time;
dtTick.Rows.Add(dr);
dr = dtTick.NewRow();
dr["TickName"] = "委買價";
dr["TickValue"] = nBid / dDigitNum;
dtTick.Rows.Add(dr);
dr = dtTick.NewRow();
dr["TickName"] = "委賣價";
dr["TickValue"] = nAsk / dDigitNum;
dtTick.Rows.Add(dr);
dr = dtTick.NewRow();
dr["TickName"] = "成交價";
dr["TickValue"] = nClose / dDigitNum;
dtTick.Rows.Add(dr);
dr = dtTick.NewRow();
dr["TickName"] = "數量";
dr["TickValue"] = nQty;
dtTick.Rows.Add(dr);
//輸出 GridView
gvTick.DataSource = dtTick;
}
else
{
// 報價物件更新 Datatable
dr = dtTick.Select("TickName='日期'")[0];
dr["TickValue"] = date;
dr = dtTick.Select("TickName='時間'")[0];
dr["TickValue"] = time;
dr = dtTick.Select("TickName='委買價'")[0];
dr["TickValue"] = nBid / dDigitNum;
dr = dtTick.Select("TickName='委賣價'")[0];
dr["TickValue"] = nAsk / dDigitNum;
dr = dtTick.Select("TickName='成交價'")[0];
dr["TickValue"] = nClose / dDigitNum;
dr = dtTick.Select("TickName='數量'")[0];
dr["TickValue"] = nQty;
}
}
我特地把它給的時間轉成習慣的格式,這種格式比較好閱讀,同時也是可以直接寫入資料庫的格式。
當接收到最新 Tick 時,就同時顯示在 DataGridView 上面。
最佳 5 檔是由最高委買價格前 5 名,以及最低委賣價格前 5 名,合併在一起的資訊。
我將這些資訊合併在同一個排名上顯示,這也是大多數券商看盤軟體常見的方式。
要訂閱最佳 5 檔跟剛剛的 Tick 是同一個呼叫函式,只要呼叫這函式後,就可以接收到 2 種回應事件。
/// <summary>
/// 訂閱 Tick & Best5
/// </summary>
private void RequestTickBest5()
{
//訂閱 Tick & Best5,訂閱後等待 OnNotifyTicks 及 OnNotifyBest5 事件回報
nCode = m_SKQuoteLib.SKQuoteLib_RequestTicks(ref sPage, ComboUtil.GetItem(cboCommID).Value);
txtMessage.AppendText(GetMessage("訂閱 Tick & Best5", nCode) + "\n");
}
建立最佳 5 檔回應事件等待觸發。
/// <summary>
/// 國內 Best5 回傳事件
/// </summary>
void m_SKQuoteLib_OnNotifyBest5(short sMarketNo, int nStockIdx, int nBestBid1, int nBestBidQty1, int nBestBid2, int nBestBidQty2, int nBestBid3, int nBestBidQty3, int nBestBid4, int nBestBidQty4, int nBestBid5, int nBestBidQty5, int nExtendBid, int nExtendBidQty, int nBestAsk1, int nBestAskQty1, int nBestAsk2, int nBestAskQty2, int nBestAsk3, int nBestAskQty3, int nBestAsk4, int nBestAskQty4, int nBestAsk5, int nBestAskQty5, int nExtendAsk, int nExtendAskQty, int nSimulate)
{
DataRow dr = null;
if (dtBest5 == null)
{
// 報價物件寫入 Datatable
dtBest5 = new DataTable();
dtBest5.Columns.Add("Seq");
dtBest5.Columns.Add("Best5BidQty");
dtBest5.Columns.Add("Best5BidPrice");
dtBest5.Columns.Add("Best5AskPrice");
dtBest5.Columns.Add("Best5AskQty");
// 價格除以 dDigitNum,是因為來的資料裡面會多 2 個 0,而預設 dDigitNum 是 100,所以要除掉 2 個 0
dr = dtBest5.NewRow();
dr["Seq"] = "1";
dr["Best5BidQty"] = nBestBidQty1;
dr["Best5BidPrice"] = nBestBid1 / dDigitNum;
dr["Best5AskQty"] = nBestAskQty1;
dr["Best5AskPrice"] = nBestAsk1 / dDigitNum;
dtBest5.Rows.Add(dr);
dr = dtBest5.NewRow();
dr["Seq"] = "2";
dr["Best5BidQty"] = nBestBidQty2;
dr["Best5BidPrice"] = nBestBid2 / dDigitNum;
dr["Best5AskQty"] = nBestAskQty2;
dr["Best5AskPrice"] = nBestAsk2 / dDigitNum;
dtBest5.Rows.Add(dr);
dr = dtBest5.NewRow();
dr["Seq"] = "3";
dr["Best5BidQty"] = nBestBidQty3;
dr["Best5BidPrice"] = nBestBid3 / dDigitNum;
dr["Best5AskQty"] = nBestAskQty3;
dr["Best5AskPrice"] = nBestAsk3 / dDigitNum;
dtBest5.Rows.Add(dr);
dr = dtBest5.NewRow();
dr["Seq"] = "4";
dr["Best5BidQty"] = nBestBidQty4;
dr["Best5BidPrice"] = nBestBid4 / dDigitNum;
dr["Best5AskQty"] = nBestAskQty4;
dr["Best5AskPrice"] = nBestAsk4 / dDigitNum;
dtBest5.Rows.Add(dr);
dr = dtBest5.NewRow();
dr["Seq"] = "5";
dr["Best5BidQty"] = nBestBidQty5;
dr["Best5BidPrice"] = nBestBid5 / dDigitNum;
dr["Best5AskQty"] = nBestAskQty5;
dr["Best5AskPrice"] = nBestAsk5 / dDigitNum;
dtBest5.Rows.Add(dr);
//輸出 GridView
gvBest5Merge.DataSource = dtBest5;
}
else
{
// 報價物件更新 Datatable
dr = dtBest5.Select("Seq='1'")[0];
dr["Best5BidQty"] = nBestBidQty1;
dr["Best5BidPrice"] = nBestBid1 / dDigitNum;
dr["Best5AskQty"] = nBestAskQty1;
dr["Best5AskPrice"] = nBestAsk1 / dDigitNum;
dr = dtBest5.Select("Seq='2'")[0];
dr["Best5BidQty"] = nBestBidQty2;
dr["Best5BidPrice"] = nBestBid2 / dDigitNum;
dr["Best5AskQty"] = nBestAskQty2;
dr["Best5AskPrice"] = nBestAsk2 / dDigitNum;
dr = dtBest5.Select("Seq='3'")[0];
dr["Best5BidQty"] = nBestBidQty3;
dr["Best5BidPrice"] = nBestBid3 / dDigitNum;
dr["Best5AskQty"] = nBestAskQty3;
dr["Best5AskPrice"] = nBestAsk3 / dDigitNum;
dr = dtBest5.Select("Seq='4'")[0];
dr["Best5BidQty"] = nBestBidQty4;
dr["Best5BidPrice"] = nBestBid4 / dDigitNum;
dr["Best5AskQty"] = nBestAskQty4;
dr["Best5AskPrice"] = nBestAsk4 / dDigitNum;
dr = dtBest5.Select("Seq='5'")[0];
dr["Best5BidQty"] = nBestBidQty5;
dr["Best5BidPrice"] = nBestBid5 / dDigitNum;
dr["Best5AskQty"] = nBestAskQty5;
dr["Best5AskPrice"] = nBestAsk5 / dDigitNum;
}
}
以上重點講解了程式碼,接下來就看一下執行後的畫面。
在執行「開始報價」之後,就會呼叫報價函式,等待連線成功之後,就會開始即時更新最新價格。
【C# 群益 API 開發教學】帳號登入、取得下單帳號教學 #CH2 (附範例)
[C#] 取得證交所台股價格的 3 種實用方法(附範例)